package com.desire.homework;

import java.io.*;

/**
 * 文件的分割与合并
 */
public class _36_文件的分卷与合并 {

    public static void main(String[] args) throws IOException {
        String srcPath = "C:\\Users\\desire\\Desktop\\javaProject\\01-javaBase\\src\\main\\resources\\哔哩哔哩_卡尔蜜拉叫你起床_1642600243235.mp3";
        String destPath = "C:\\Users\\desire\\Desktop\\javaProject\\01-javaBase\\src\\main\\resources\\cut\\";
        fileSplit(srcPath, destPath, 20, 1024, "mp3");
        /*================================================================*/
        //String srcPath = "C:\\Users\\desire\\Desktop\\javaProject\\01-javaBase\\src\\main\\resources\\cut\\";
        //String destPath = "C:\\Users\\desire\\Desktop\\javaProject\\01-javaBase\\src\\main\\resources\\cut\\combine.mp3";
        //fileCombine(srcPath, destPath, 20, 1024, "mp3");

    }

    /**
     * 文件的切割
     *
     * @param srcPath  切割源文件路径
     * @param destPath 合并文件路径
     * @param size     切割块数
     * @param speed    切割速度
     * @param suffix   切割文件后缀
     * @throws IOException 异常处理
     */
    public static void fileSplit(String srcPath, String destPath,
                                 int size, int speed, String suffix) throws IOException {
        long start = System.currentTimeMillis();
        // 多态写法
        InputStream is = new FileInputStream(srcPath);
        //获取文件可读取的字节数，依据这个对文件进行分割
        // .available()在读写操作前先得知数据流里有多少个字节
        int total = is.available();
        // System.out.println(total); // 349756÷20 = 17487.8  一卷
        //文件分块切割数目 size，余数放到最后一个文件中。
        int[] blocks = new int[size];
        for (int i = 0; i < size; i++) {
            if (i == size - 1) {
                // total/size :一卷n个字节   total%size : 最后一卷的字节数
                blocks[i] = total / size + total % size;
            } else {
                // 刚好整除分卷
                blocks[i] = total / size;
            }
        }
        //定义创建 size 个输出流的数组.

        OutputStream[] os = new OutputStream[size];
        for (int i = 0; i < os.length; i++) {
            os[i] = new FileOutputStream(destPath + i + suffix);
        }
        // 到这里  只是依照传入的size 创建出了 size个文件  里边还没有写入字节


        // 开始往创建好的文件中写入数据
        int index = 0;
        //表示要读取到哪一块，一块一块地读取
        int remain = blocks[index];
        //记录当次需要读取多少个字节
        int needRead;
        int len = 0;
        //以传入的speed 创建缓冲区大小
        byte[] buff = new byte[speed];
        // index=0 < size=20
        while (index < size) {
            //处理数组越界问题  找到 remain speed最小的值 以最小的值为读取字节数
            needRead = Math.min(remain, speed);
            //读取数据 B -数据被读入的缓冲区。 Off -数组b中写入数据的起始偏移量。 Len -要读取的最大字节数
            len = is.read(buff, 0, needRead);
            if (len != -1) {
                //写出数据、读多少, 写多少 B -数据。 Off -数据中的起始偏移量。 Len -要写入的字节数
                os[index].write(buff, 0, len);
                //强制刷新数据
                os[index].flush();
                //读多少减多少
                remain = remain - len;
                //索引指向下一个文件块
                if (remain == 0) {
                    index++;
                    if (index < size) {
                        remain = blocks[index];
                    }
                }
            }
        }
        is.close();
        long end = System.currentTimeMillis();
        System.out.println("执行文件切割耗时： " + (end - start) / 1000 + " s");
        System.out.println("程序结束 ...");
    }

    /**
     * 文件的合并
     *
     * @param srcPath  分散文件源文件路径
     * @param destPath 合并成功后文件的存放路径
     * @param size     源文件数目
     * @param speed    合并速度
     * @param suffix   合并文件后缀
     * @throws IOException 异常处理
     */
    public static void fileCombine(String srcPath, String destPath,
                                   int size, int speed, String suffix) throws IOException {
        long start = System.currentTimeMillis();
        //目标输出流   输出的追加的后边  多态写法
        OutputStream os = new FileOutputStream(destPath, true);
        //定义创建size 个输入流的数组.
        InputStream[] is = new InputStream[size];
        for (int i = 0; i < size; i++) {
            is[i] = new FileInputStream(srcPath + i + suffix);
        }
        // ↑ 创建好了一个可供写入的文件


        // ↓ 往创建好的文件内  写入数据
        //获取每个文件大小
        int[] blocks = new int[size];
        for (int i = 0; i < size; i++) {
            blocks[i] = is[i].available();
        }
        //一次
        int needRead;
        //文件号索引
        int index = 0;
        //记录读取到哪个文件
        int remain = blocks[index];
        //缓冲，每次读取的大小
        byte[] buff = new byte[speed];
        //接收数据
        int len = 0;
        while (index < size) {
            //剩余数 ...
            needRead = Math.min(remain, speed);
            //读一次，写一次  如果字节够，则将byte数组中的数据读完，否则读剩下的。
            len = is[index].read(buff, 0, needRead);
            //边读取边输出到本地文件中，读多少，输出多少
            if (len != -1) {
                //off  需要一个标签来记录,将文件输入到指定文件中。
                os.write(buff, 0, len);
                //读多少，减多少，直到文件大小被减为0 ，则表明文件被读取完毕，接着判断读取下一个文件
                remain = remain - len;
            }
            //表明文件被读取完毕，接着判断读取下一个文件
            if (remain == 0) {
                //记录文件号
                index++;
                if (index < size) {
                    remain = blocks[index];
                }
            }
        }
        os.close();
        long end = System.currentTimeMillis();
        System.out.println("执行文件合并耗时： " + (end - start) / 1000 + " s");
        System.out.println("程序结束 ...");
    }
}
